package passInfo

import (
	"car-thirdparty-api/api/passInfo/v1"
	"car-thirdparty-api/internal/model"
	"context"
	"fmt"
	"github.com/gogf/gf/v2/encoding/gbase64"
	"github.com/gogf/gf/v2/encoding/gcharset"
	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/jackc/pgx/v4"
	"github.com/jackc/pgx/v4/pgxpool"
	"io"
	"os"
	"strings"
	"time"
)

func (c *ControllerV1) PassInfo(ctx context.Context, req *v1.PassInfoReq) (res *v1.PassInfoRes, err error) {
	// 如果校区id为空，则无法选择对应校区，所以直接返回校区不能为空的错误
	if req.StartTime != "" && req.EndTime != "" {
		startTime, _ := time.Parse("2006-01-02 15:04:05", req.StartTime)
		endTime, _ := time.Parse("2006-01-02 15:04:05", req.EndTime)
		if endTime.Before(startTime) {
			//r.Response.Write("结束时间不能小于开始时间")
			g.Log().Error(ctx, "结束时间不能小于开始时间")
			return nil, gerror.NewCode(gcode.CodeValidationFailed, "结束时间不能小于开始时间")
		}
	}

	// 根据schoolId从schoolMap中获取数据库名称
	if schoolName, ok := SchoolMap[req.SchoolId]; ok {
		// goframe切换数据源为schoolName
		var list []model.PassInfoItem
		sql := "select r.history_seq,r.event_time as passTime, r.gate_id, r.channel_id, r.channel_type,encode(r.plate_no::bytea,'base64') as plate,r.card_no, r.channel_credit, r.identity_type, r.usercode, r.image_full, r.image_plate as passImgId,r.image_face,r.status, r.corrected, r.match_seq, r.pass_status, r.custom_type,r.remarks, r.pass_type, r.park_time, encode(u.username::bytea,'base64') as userName,encode(c.channel_name::bytea,'base64') as passChannel from ipms_history_rec r left join ipms_user u on r.usercode = u.usercode left join ipms_channel c on r.channel_id = c.channel_id"
		//if req.Plate != "" {
		//	// 车牌转base64精确查询
		//	//plate, _ := gcharset.Convert("GBK", "UTF-8", req.Plate)
		//	//plate = gbase64.EncodeString(plate)
		//	//sql += " where encode(r.plate_no::bytea,'base64') = '" + plate + "'"
		//
		//	// 直接转GBK模糊查询
		//	plate, _ := gcharset.Convert("GBK", "UTF-8", req.Plate)
		//	//sql += " where r.plate_no like '%" + req.Plate + "%'"
		//	sql += " where r.plate_no like '%" + plate + "%'"
		//}
		db := g.DB(schoolName)
		m := db.Raw(sql)
		if req.Plate != "" {
			// 直接转GBK模糊查询
			m = m.WhereLike("r.plate_no", "%"+req.Plate+"%")
		}
		m = m.WhereBetween("r.event_time", req.StartTime, req.EndTime)
		err = m.OrderDesc("r.event_time").Limit(5).Scan(&list)
		if err != nil {
			g.Log().Error(ctx, "查询数据库失败，错误信息为：", err)
			return nil, err
		}
		g.Log().Debug(ctx, "查询结果为：", list)

		res = new(v1.PassInfoRes)
		res.PassInfoRes = new(model.PassInfoRes)
		if len(list) > 0 {
			pgConf := db.GetConfig()
			var dsnBuilder strings.Builder
			dsnBuilder.WriteString("postgres://" + pgConf.User + ":" + pgConf.Pass + "@" + pgConf.Host + ":" + pgConf.Port + "/" + pgConf.Name)
			dsn := dsnBuilder.String()
			g.Log().Debug(ctx, "dsn为：", dsn)

			// pgx
			//conn, err := pgxpool.Connect(ctx, "postgres://ipms:ipms@119.29.100.33:5432/ipms")
			conn, err1 := pgxpool.Connect(ctx, dsn)
			if err1 != nil {
				fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
				return
			}
			defer conn.Close()

			resList := make([]model.PassInfoItem, len(list))
			for i, v := range list {
				//item := resList[i]

				// 将plate通过base64解码然后用GBK编码
				plate, _ := gbase64.DecodeString(v.Plate)
				uPlate, _ := gcharset.Convert("UTF-8", "GBK", string(plate))
				resList[i].Plate = uPlate

				bChannel, _ := gbase64.DecodeString(v.PassChannel)
				uChannel, _ := gcharset.Convert("UTF-8", "GBK", string(bChannel))
				resList[i].PassChannel = uChannel

				bUserName, _ := gbase64.DecodeString(v.UserName)
				uUserName, _ := gcharset.Convert("UTF-8", "GBK", string(bUserName))
				resList[i].UserName = uUserName
				resList[i].PassTime = v.PassTime
				resList[i].PassImgId = v.PassImgId

				if v.PassImgId == 0 {
					g.Log().Error(ctx, "大对象id为0，跳过大对象获取")
					continue
				}

				// pgx
				// 开始事务
				tx, err := conn.Begin(context.Background())
				if err != nil {
					fmt.Println("Failed to start transaction:", err)
					//tx.Rollback(context.Background())
					continue
				}
				defer tx.Rollback(context.Background())

				// 获取 LargeObjects 实例
				largeObjects := tx.LargeObjects()
				//if largeObjects == nil {
				//	fmt.Println("Failed to get LargeObjects instance")
				//	return
				//}
				g.Log().Debug(ctx, "ObjectId：", v.PassImgId)
				largeObject, err := largeObjects.Open(context.Background(), v.PassImgId, pgx.LargeObjectModeRead)
				if err != nil {
					fmt.Println("Failed to open large object:", err)
					//largeObject.Close()
					continue
				}
				defer largeObject.Close()

				// 读取大对象内容
				var data []byte
				buffer := make([]byte, 20480) // 设置读取块大小
				for {
					n, err := largeObject.Read(buffer)
					if err != nil && err != io.EOF {
						fmt.Println("Failed to read from large object:", err)
						break
					}
					if n == 0 {
						break // 读取完成
					}

					data = append(data, buffer[:n]...)
					//fmt.Printf("Read %d bytes: %s\n", n, buffer[:n])
				}

				// 提交事务
				if err := tx.Commit(context.Background()); err != nil {
					fmt.Println("Failed to commit transaction:", err)
					continue
				}
				//err = gfile.PutBytes("./test.jpg", data)
				//if err != nil {
				//	g.Log().Error(ctx, "保存大对象数据出错：", err)
				//}

				resList[i].PassImg = gbase64.EncodeToString(data)
				g.Log().Debug(ctx, "大对象数据为：", v.PassImg)

				//resList[i] = item
			}

			res.PassInfoRes.List = resList
		} else {
			return nil, gerror.NewCode(gcode.CodeNotFound, "未查询到通行记录")
		}
	} else {
		g.Log().Error(ctx, "未能找到对应校区，校区id为：", req.SchoolId)
		return nil, gerror.NewCode(gcode.CodeNotFound, "校区不存在")
	}

	g.Log().Debug(ctx, "查询结果: ", res)
	if res != nil {
		if res.PassInfoRes != nil {
			res.PassInfoRes.Total = len(res.PassInfoRes.List)
		}
		return res, nil
	} else {
		g.Log().Error(ctx, "未能找到对应校区的记录，校区id为：", req.SchoolId)
		return nil, gerror.NewCode(gcode.CodeNotFound)
	}
}

func (c *ControllerV1) getLargeObject(ctx context.Context, oid int64, conn *pgx.Conn) (data string, err error) {
	// 打开大对象
	//largeObjectAPI := conn.LargeObjects(ctx)
	//obj, err := largeObjectAPI.Open(oid, pgconn.LargeObjectModeRead)
	//if err != nil {
	//	fmt.Println("打开大对象出错：", err)
	//	return
	//}
	//defer obj.Close()
	//
	//// 读取大对象的内容
	//bData := make([]byte, obj.Size())
	//_, err = obj.Read(bData)
	//if err != nil {
	//	fmt.Println("读取大对象出错：", err)
	//	return
	//}
	//
	//// 输出二进制数据
	//fmt.Println("二进制数据：", data)

	// 使用pgx获取postgress中的大对象
	//var data []byte
	//err = conn.QueryRow(ctx, "SELECT lo_get($1)", oid).Scan(&data)
	return
}
